﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using Cornerstone.ScraperEngine;
using System.Text.RegularExpressions;
using System.Drawing;

namespace MyEmulators2
{
    /// <summary>
    /// A wrapper for a specified script, provides
    /// methods to download and update RomMatch info.
    /// The specified Importer's DoWork is polled
    /// periodically during invocation
    /// </summary>
    class Import_Scraper
    {
        bool isReady = false;
        /// <summary>
        /// Is true if specified script was loaded successfully
        /// </summary>
        public bool IsReady
        {
            get { return isReady; }
        }

        bool doWork()
        {
            if (importer == null)
                return true;
            else
                return importer.DoWork;
        }

        ScriptableScraper scraper = null;
        Importer importer = null;

        bool importTop = false;
        bool importExact = false;

        public Import_Scraper(Script script, Importer importer)
        {
            this.importer = importer;

            //try and load a valid scraper
            Assembly asm = Assembly.GetExecutingAssembly();
            string scriptTxt = "";
            Stream scriptStream = null;

            //get specified script
            switch (script)
            {
                case Script.MobyGames:
                    scriptStream = asm.GetManifestResourceStream("MyEmulators2.Scripts.Mobygames.xml");
                    break;
                case Script.Maws:
                    scriptStream = asm.GetManifestResourceStream("MyEmulators2.Scripts.Maws.xml");
                    break;
            }

            if (scriptStream == null)
                return;
            
            using (StreamReader sr = new StreamReader(scriptStream))
            {
                scriptTxt = sr.ReadToEnd();
            }

            scraper = new ScriptableScraper(scriptTxt, false);
            if (!scraper.LoadSuccessful)
            {
                Logger.LogError("Import_Scraper - Error loading specified script");
                scraper = null;
                return; //problem with scraper
            }

            Update();

            isReady = true;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Update()
        {
            importTop = Options.Instance.GetBoolOption("importtop");
            importExact = Options.Instance.GetBoolOption("importexact");
        }

        public List<ScraperResult> GetResults(string searchString)
        {
            if (!isReady)
                return new List<ScraperResult>();

            Dictionary<string, string> paramList = new Dictionary<string, string>();
            paramList["search.title"] = searchString;

            return getSearchResults(paramList);
        }

        /// <summary>
        /// Updates the specified RomMatch with a list of possible matches.
        /// If a close match is found the RomMatch is approved
        /// </summary>
        /// <param name="romMatch"></param>
        public void GetMatches(RomMatch romMatch)
        {
            if (!isReady)
                return;

            //get clean search term
            string searchString = StripRomCodes(romMatch.Title).ToLower();

            Dictionary<string, string> paramList = new Dictionary<string, string>();
            paramList["search.title"] = searchString;

            List<ScraperResult> results = getSearchResults(paramList);

            if (!doWork())
                return;

            //get parent title to try and match platform
            string searchSystem = romMatch.Game.ParentEmulator.Title.ToLower();

            int possibleTitleMatches = 0;
            ScraperResult titleSite = null;

            int roughTitleMatches = 0;
            ScraperResult roughTitleSite = null;

            int possibleSystemMatches = 0;
            ScraperResult systemSite = null;

            int roughSystemMatches = 0;
            ScraperResult roughSystemSite = null;

            bool isExact = false;

            foreach (ScraperResult result in results)
            {
                if (!doWork())
                    return;

                if (result.Title.ToLower() == searchString && result.System.ToLower() == searchSystem)
                {
                    //exact match break and get details
                    isExact = true;
                    possibleTitleMatches = 1;
                    titleSite = result;
                    break;
                }
                else if (result.Title.ToLower() == searchString)
                {
                    //if it's the first title match, set
                    if (possibleTitleMatches == 0)
                        titleSite = result;
                    possibleTitleMatches++;
                }
                else if (result.Title.ToLower().Contains(searchString) || searchString.Contains(result.Title.ToLower()))
                {                    
                    if (roughTitleMatches == 0)
                        roughTitleSite = result;
                    roughTitleMatches++;
                }
                else if (result.System.ToLower() == searchSystem)
                {
                    //if it's the first platform match, set
                    if (possibleSystemMatches == 0)
                        systemSite = result;
                    possibleSystemMatches++;
                }
                else if (result.System.ToLower().Contains(searchSystem) || searchSystem.Contains(result.System.ToLower()))
                {
                    if (roughSystemMatches == 0)
                        roughSystemSite = result;
                    roughSystemMatches++;
                }
            }

            ScraperResult matchSite = null;
            if (possibleTitleMatches == 1 && (isExact || !importExact))
                matchSite = titleSite;
            else if (possibleSystemMatches == 1 && !importExact)
                matchSite = systemSite;
            else if (results.Count > 0 && importTop)
                matchSite = results[0];

            if (!doWork())
                return;

            if (matchSite != null) //approved match
            {
                romMatch.GameDetails = matchSite;
                romMatch.Approved = true;
            }
            else
            {
                //select best match but don't approve
                romMatch.Approved = false;

                if (titleSite != null)
                    romMatch.GameDetails = titleSite;
                else if (systemSite != null)
                    romMatch.GameDetails = systemSite;
                else if (roughTitleSite != null)
                    romMatch.GameDetails = roughTitleSite;
                else if (roughSystemSite != null)
                    romMatch.GameDetails = roughSystemSite;
                else if (results.Count > 0)
                    romMatch.GameDetails = results[0];
            }
            romMatch.InfoChecked = true;
            romMatch.PossibleGameDetails = results.ToArray();
        }

        /// <summary>
        /// Retrieves info and thumbs based on romMatch.GameDetails
        /// and updates romMatch.ScraperGame
        /// </summary>
        /// <param name="romMatch"></param>
        public void DownloadInfo(RomMatch romMatch)
        {
            if (!isReady)
                return;

            if (romMatch.GameDetails == null)
                return;

            Dictionary<string, string> paramList = new Dictionary<string, string>();
            paramList["game.site_id"] = romMatch.GameDetails.SiteId;

            //retrieve Game info
            ScraperGame scraperGame = getGame(paramList);
            if (!doWork() || scraperGame == null)
                return;

            //retrieve cover urls
            Dictionary<string, string> results = scraper.Execute("get_cover_art", paramList);

            if (!doWork())
                return;

            if (results != null)
            {

                string covers = "";

                if (results.TryGetValue("game.covers", out covers))
                {
                    string[] coverurls = covers.Split('|');

                    bool foundFront = false;
                    bool foundBack = false;
                    for (int i = 1; i < coverurls.Length; i++)
                    {
                        if (!foundFront) 
                        {
                            scraperGame.BoxFront = getAndCheckImage(coverurls[i], ref foundFront);
                            //scraperGame.BoxFrontUrl = coverurls[i];
                        }
                        else if (!foundBack)
                        {
                            scraperGame.BoxBack = getAndCheckImage(coverurls[i], ref foundBack);
                            //scraperGame.BoxBackUrl = coverurls[i];
                        }
                        if (!doWork())
                            return;
                        if (foundFront && foundBack)
                            break;
                    }
                }
            }

            //get screenshot urls
            results = scraper.Execute("get_screenshots", paramList);

            if (!doWork())
                return;

            if (results != null)
            {

                string screens = "";
                string screenshotsbaseurl = "";

                results.TryGetValue("game.screenshotsbaseurl", out screenshotsbaseurl);

                if (results.TryGetValue("game.screenshots", out screens))
                {
                    string[] screenurls = screens.Split('|');

 

                    bool foundTitleScreen = false;
                    bool foundInGame = false;

                    //loop through urls and try and select best match
                    for (int i = 1; i < screenurls.Length; i++)
                    {
                        //update url if relative
                        if (!screenurls[i].ToLower().StartsWith("http://"))
                        {
                            screenurls[i] = screenshotsbaseurl + screenurls[i];
                        }

                        //prefer this filename for Title screen
                        if (screenurls[i].Contains("screenshot-start-screen"))
                        {
                            scraperGame.TitleScreen = getAndCheckImage(screenurls[i], ref foundTitleScreen);
                        }
                        //use this if above filename not yet found
                        else if (screenurls[i].Contains("screenshot-title-screen"))
                        {
                            if (!foundTitleScreen)
                            {
                                scraperGame.TitleScreen = getAndCheckImage(screenurls[i], ref foundTitleScreen);
                            }
                        }
                        //else set first image to Title screen
                        else if (scraperGame.TitleScreen == null)
                        {
                            scraperGame.TitleScreen = getAndCheckImage(screenurls[i], ref foundTitleScreen);
                            foundTitleScreen = false;
                        }
                        //set first image not covered by above to Ingame
                        else if (scraperGame.InGame == null)
                        {
                            scraperGame.InGame = getAndCheckImage(screenurls[i], ref foundInGame);
                        }

                        if (!doWork())
                            return;

                        if (foundTitleScreen && foundInGame)
                            break;
                    }
                }
            }

            romMatch.ScraperGame = scraperGame;
        }

        public void GetThumbs(RomMatch romMatch, out string[] covers, out string[] screens)
        {
            covers = new string[] { };
            screens = new string[] { };

            if (!isReady)
                return;

            if (romMatch.GameDetails == null)
                return;

            Dictionary<string, string> paramList = new Dictionary<string, string>();
            paramList["game.site_id"] = romMatch.GameDetails.SiteId;

            //retrieve cover urls
            Dictionary<string, string> results = scraper.Execute("get_cover_art", paramList);

            if (results != null)
            {
                string coverUrls = "";

                if (results.TryGetValue("game.covers", out coverUrls))
                {
                    covers = coverUrls.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                }
            }

            //get screenshot urls
            results = scraper.Execute("get_screenshots", paramList);

            if (results != null)
            {
                string screenUrls;
                string screenshotsbaseurl = "";
                results.TryGetValue("game.screenshotsbaseurl", out screenshotsbaseurl);

                if (results.TryGetValue("game.screenshots", out screenUrls))
                {
                    screens = screenUrls.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                }

                for (int x = 0; x < screens.Length; x++)
                {
                    if (!screens[x].ToLower().StartsWith("http://"))
                        screens[x] = screenshotsbaseurl + screens[x];
                }
            }
        }

        //execute scraper search using specified param's, return results
        List<ScraperResult> getSearchResults(Dictionary<string, string> paramList)
        {
            List<ScraperResult> results = new List<ScraperResult>();
            if (scraper == null)
                return results;

            Dictionary<string, string> scraperResults = scraper.Execute("search", paramList);
            if (!doWork() || scraperResults == null)
            {
                return results;
            }

            string respage = "";

            scraperResults.TryGetValue("search_page", out respage);
            int count = 0;

            //loop and build results
            while (scraperResults.ContainsKey("game[" + count + "].site_id"))
            {
                if (!doWork())
                    return results;

                string siteId;
                string title;
                string yearmade;
                string system;
                string xsiteId;
                string xgamePattern;
                string prefix = "game[" + count + "].";
                count++;


                if (!scraperResults.TryGetValue(prefix + "site_id", out siteId))
                    continue;

                if (scraperResults.TryGetValue(prefix + "system", out system))
                    system = system.Trim();
                scraperResults.TryGetValue(prefix + "title", out title);
                scraperResults.TryGetValue(prefix + "yearmade", out yearmade);
                scraperResults.TryGetValue(prefix + "extraGameUrl", out xsiteId);
                scraperResults.TryGetValue(prefix + "extraGamePattern", out xgamePattern);

                if (!string.IsNullOrEmpty(xsiteId))
                    siteId = xsiteId;

                results.Add(new ScraperResult(siteId, title, system, yearmade, this));

                //additional field added as mobygames combines results from different platforms so we need
                //to split them to get the correct details/thumbs for our platform
                if (!string.IsNullOrEmpty(xgamePattern))
                {
                    //split combined results from mobygames
                    foreach (Match match in new Regex(@"<a href=""/game/([^""]*)"">([^<]*)</a> \(<em>(\d{4})").Matches(xgamePattern))
                    {
                        results.Add(new ScraperResult(match.Groups[1].Value, title, match.Groups[2].Value, match.Groups[3].Value, this));
                    }
                }
            }

            return results;
        }

        //download and return info
        ScraperGame getGame(Dictionary<string, string> paramList)
        {
            if (scraper == null)
                return null;

            Dictionary<string, string> results = scraper.Execute("get_details", paramList);
            if (!doWork() || results == null)
            {
                return null;
            }

            string grade;
            string title;
            string yearmade;
            string company;
            string description;
            string genre;

            results.TryGetValue("game.grade", out grade);
            results.TryGetValue("game.title", out title);
            results.TryGetValue("game.yearmade", out yearmade);
            results.TryGetValue("game.company", out company);
            results.TryGetValue("game.description", out description);
            results.TryGetValue("game.genre", out genre);
            if (!string.IsNullOrEmpty(genre) && genre.StartsWith("|"))
            {
                genre = genre.Remove(0, 1);
            }

            return new ScraperGame(title, company, yearmade, grade, StripTags(description), genre);
        }

        string StripRomCodes(string title)
        {
            Regex objRegEx = new Regex(@"\(.*?\)");
            return objRegEx.Replace(title, "");
        }

        string StripTags(string HTML)
        {
            if (string.IsNullOrEmpty(HTML))
                return "";

            HTML = HTML.Replace("<br>", System.Environment.NewLine);
            HTML = HTML.Replace("<br />", System.Environment.NewLine);
            HTML = HTML.Replace("<br/>", System.Environment.NewLine);

            System.Text.RegularExpressions.Regex objRegEx = new System.Text.RegularExpressions.Regex("<[^>]*>");
            return objRegEx.Replace(HTML, "");
        }

        Bitmap getAndCheckImage(string url, ref bool success)
        {
            Bitmap img = ImageHandler.Instance.BitmapFromWeb(url);
            success = img != null ? true : false;
            return img;
        }
    }

    internal enum Script
    {
        MobyGames,
        Maws
    }
}
